#define _GNU_SOURCE
#include "app_device.h"
#include "app_mqtt.h"
#include "app_message.h"
#include "app_buffer.h"
#include "app_pool.h"
#include "log/log.h"
#include "app_common.h"
#include "app_bt.h"
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>

#define BUFFER_SIZE 1024

static Device *device;  //因为只有一个设备（蓝牙），所以用静态变量

//内部使用任务、回调函数等——————————————————————————————

/* 
- 上行程序操作流程
	启动读取下游设备线程的线程，在线程函数中  read_thread_func     
		不断读取下流设备通过蓝牙连接传输过来的数据  read(fd)
		对蓝牙数据进行处理转换为字符数组消息  after_read() 
		将字符数组消息保存到上行缓冲区		app_buffer_write(up_buffer)
		将发消息数据到远程的任务交给线程池执行   app_pool_registerTask(send_task_func)
	在任务函数中  send_task_func
		从上行缓冲中读取出一个字符数组消息	app_buffer_read(up_buffer)
		将字符数组消息转换为json格式消息	  app_message_charsToJson()
		将json消息发送给远程			  app_mqtt_send(json) */

//线程池任务：发送数据到mqtt(0成功，-1失败)
static int send_task_func(void *arg){
    Device *device=(Device *)arg;
    //1.从上行缓冲区中取出一个字符数组消息
    //(数据添加到缓冲是因为多个数据通过多线程同时发送到mqtt)
    char r_buf[128];
    int recv_date_len= app_buffer_read(device->up_buffer,r_buf,sizeof(r_buf));
    //2.将字符数组消息转换为json格式消息
    char *json=app_message_charsToJson(r_buf,recv_date_len); 
    //3.将json消息发送给远程
    int result= app_mqtt_send(json);
    if(result==-1){
        log_error("发送数据到远程失败\n");
        return -1;
    }
    log_debug("发送数据到远程成功: %s\n",json);
    return 0;
}

//读取线程任务        
static void *read_thread_func(void *arg){
    while(device->is_running){
        //1.读取数据
        char recv_buffer[128];
        ssize_t char_size= read(device->fd,recv_buffer,sizeof(recv_buffer));
        //2.数组中有数据并且转换函数存在，将数据转为字符
        if((char_size>0)&&(device->after_read!=NULL)){
            //recv_buffer是字符数组，char_size是数据长度
            char_size= device->after_read(recv_buffer,char_size);
        }
        //3.将字符数组消息保存到上行缓冲区(为了多个数据通过多线程同时发送到mqtt)
        if(char_size>0){  //判断内容有设备后可以放上面if
            app_buffer_write(device->up_buffer,recv_buffer,char_size);
            //4.将发消息数据到远程的任务交给线程池执行
            app_pool_registerTask(send_task_func,device);
        }
    }
}

/* 下行程序操作流程
	注册接收远程消息的回调函数  app_mqtt_registerRecvCallback(receive_msg_callback)
		将json格式消息转换为字符数组消息 app_message_jsonToChars()
		将其保存到下行缓冲中  app_buffer_write(down_buffer)
		将消息数据保存到设备文件的任务交给线程池执行 app_pool_regiserTask(write_task_func)
	在任务函数中 write_task_func
		从下行缓冲中读取出一个字符数组消息  app_buffer_read(down_buffer)
		将字符数组消息转换为蓝牙需要的格式数据  pre_write()
		将数据保存到蓝牙对应的串口文件中，传递给下游设备 write(fd) */

static int write_task_func(void *arg){
    //1.从下行缓冲中读取出一个字符数组消息
    Device *device=(Device *)arg;
    char r_buf[128];
    int recv_date_len= app_buffer_read(device->down_buffer,r_buf,sizeof(r_buf));
    //1.5判断数据是否为空
    if(recv_date_len==-1){
        log_error("下行缓冲区读取数据失败\n");
        return -1;
    }
    //2.将字符数组消息转换为蓝牙需要的格式数据
    if(device->pre_write!=NULL&&recv_date_len>0){
        //将char数组转为原始数据发送到设备(数据存r_buf中,数据长度返回)
        recv_date_len= device->pre_write(r_buf,recv_date_len);

    }
    //2.5写入前要判断时间间隔大于200ms才可以
    long distance_time=app_common_getCurrentTime()-device->last_write_time;
    if(distance_time<200){
        //睡微秒，需要引入头文件unistd#define _GNU_SOURCE
        usleep((200-distance_time)*1000);
    }

    //3.将数据保存到蓝牙对应的串口文件中，传递给下游设备
    int result= write(device->fd,r_buf,recv_date_len);
    if(result==-1){
        log_error("将数据保存到蓝牙对应的串口文件中失败\n");
    }
    log_debug("将数据保存到蓝牙对应的串口文件中成功\n");
    //3.5记录最后一次写入时间
    device->last_write_time=app_common_getCurrentTime();
    return 0;
}

//将远程数据转换发送到缓冲区，启动多线程
static int receive_msg_callback(char *json){
    //1.将json格式消息转换为字符数组消息
    char r_buf[128];
    int recv_date_len= app_message_JsonTochars(json,r_buf,128);
    //2.将其保存到下行缓冲中(方便多线程同时读取数据，转换格式，发送到设备)
    app_buffer_write(device->down_buffer,r_buf,recv_date_len);
    //3.将消息数据保存到设备文件的任务交给线程池执行
    app_pool_registerTask(write_task_func,device);
    return 0;
}

//外部调用函数————————————————————————————————————————

//初始化:创建线程池、缓冲区、初始化mqtt客户端
Device * app_device_init(char *filename){
    //结构体分配空间
    device=(Device *)malloc(sizeof(Device));
    //结构体属性
    device->filename=filename;              //文件名要连接设备
    device->fd=open(filename,O_RDWR,0644);  //文件存在读写、权限
    device->up_buffer=app_buffer_init(BUFFER_SIZE); //缓冲区大小
    device->down_buffer=app_buffer_init(BUFFER_SIZE); 
    //线程等启动函数才创建
    device->is_running=0;                   //默认读线程未启动
    device->last_write_time=0;              //默认最后一次写时间0
    device->after_read=NULL;                //默认读回调函数为空  
    device->pre_write=NULL;                 //默认写回调函数为空

    //启动mqtt客户端
    int mqtt_result= app_mqtt_init();
    if(mqtt_result==0){
        log_debug("mqtt客户端初始化成功\n");
    }
    //启动线程池:5个线程
    int pool_result= app_pool_init(5);
    if(pool_result==0){
        log_debug("线程池初始化成功\n");
    }

    return device;
}

//销毁:关闭文件描述符、销毁缓冲区、销毁线程池、销毁mqtt客户端
void app_device_destroy(Device *device){
    if(device->is_running){
        pthread_cancel(device->read_thread);
        pthread_join(device->read_thread,NULL);  //插队结束线程
    }
    close(device->fd);
    app_buffer_free(device->up_buffer);
    app_buffer_free(device->down_buffer);
    free(device);
    app_pool_close();
    app_mqtt_close();
}

//启动设备：防止多次调用、创建读线程（把设备数据转格式发到mqtt）、mqtt接收（把mqtt数据转为设备数据）
int app_device_start(Device *device){
    //防止多次启动
    if(device->is_running){
        return -1;
    }
    device->is_running=1;
    //启动读线程(上行流程)
    pthread_create(&device->read_thread,NULL,read_thread_func,NULL);
    //启动mqtt接收(下行流程)
    app_mqtt_registRecvCallback(receive_msg_callback);
    return 0;
}
